/**
* @Auther:gy
* @Date:2021/8/13 11:36
 */

package tool

import (
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"time"
)

type ClaimCli struct {
	Secret     []byte        `json:"secret" comment:"秘钥"`
	ExpireTime time.Duration `json:"time" comment:"token过期时间"`
	IsPrefix   bool          `json:"isPrefix" comment:"是否携带Bearer "`
}
type UserClaims struct {
	UserInfo map[string]interface{}
	jwt.StandardClaims
}

const Authorization = "Authorization"

var (
	defaultSecret     = "defaultSecret"
	defaultExpireTime = 2 * time.Hour
)

type Claims interface {
	apply(claim *ClaimCli)
}
type ClaimFunc func(claim *ClaimCli)

func WithSecret(secret string) ClaimFunc {
	return func(claim *ClaimCli) {
		claim.Secret = []byte(secret)
	}
}
func WithExpireTime(expireTime time.Duration) ClaimFunc {
	return func(claim *ClaimCli) {
		claim.ExpireTime = expireTime
	}
}
func WithBearer() ClaimFunc {
	return func(claim *ClaimCli) {
		claim.IsPrefix = true
	}
}
func NewClaim(opts ...ClaimFunc) *ClaimCli {
	claim := &ClaimCli{
		Secret:     []byte(defaultSecret),
		ExpireTime: defaultExpireTime,
	}
	for _, v := range opts {
		v(claim)
	}
	return claim
}
func (c *ClaimCli) GenerateUserToken(user map[string]interface{}) (string, error) {
	nowTime := time.Now()
	expireTime := nowTime.Add(c.ExpireTime)
	claims := UserClaims{
		user,
		jwt.StandardClaims{
			ExpiresAt: expireTime.Unix(),
			Issuer:    "dh-go-chat",
		},
	}
	tokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	token, err := tokenClaims.SignedString(c.Secret)
	if c.IsPrefix {
		token = fmt.Sprintf("Bearer %v", token)
	}
	return token, err
}
func (c *ClaimCli) ParseToken(token string) (*UserClaims, error) {
	if c.IsPrefix && len(token) >= 7 {
		token = token[7:]
	}
	tokenClaims, err := jwt.ParseWithClaims(token, &UserClaims{}, func(token *jwt.Token) (i interface{}, e error) {
		return c.Secret, nil
	})

	if tokenClaims != nil {
		if claims, ok := tokenClaims.Claims.(*UserClaims); ok && tokenClaims.Valid {
			return claims, nil
		}
	}
	return nil, err
}
func (c *ClaimCli) GetUserInfo(token string) (map[string]interface{}, error) {
	claims, err := c.ParseToken(token)
	if err != nil {
		return nil, err
	}
	return claims.UserInfo, nil
}

//如果获取多个用户信息 建议使用GetUserInfo
func (c *ClaimCli) GetUserInfoByKey(key string, token string) (interface{}, error) {
	claims, err := c.ParseToken(token)
	if err != nil {
		return nil, err
	}
	return claims.UserInfo[key], nil
}
